function sealed(constructor: Function) {
  Object.seal(constructor)
  Object.seal(constructor.prototype)
}

function f() {
  console.log('f(): evaluated')
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('f(): called')
  }
}

function g() {
  console.log('g(): evaluated')
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    console.log('g(): called')
  }
}

function nonenumerable() {
  return function (target, propertyKey) {
    console.log('nonenumerable', propertyKey)
    Object.defineProperty(target, propertyKey, {
      get: function () {
        return undefined
      },
      set: function (val) {
        const d = Object.getOwnPropertyDescriptor(this, propertyKey)
        console.log(`nonenumerable set ${val}`, d)
        Object.defineProperty(this, propertyKey, {
          value: val,
          writable: true,
          enumerable: false,
          configurable: true,
        })
      },
      enumerable: false,
    })
  }
}

function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value
  }
}

function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value
  }
}

function ss(value: boolean) {
  return function (target: any, propertyKey: string, ...rest) {
    const [ descriptor ] = rest
    // console.log(target, propertyKey, descriptor)
    if (descriptor) {
      descriptor.enumerable = value
    } else {
      Object.defineProperty(target, propertyKey, {
        configurable: true,
        writable: true,
        enumerable: value,
        value: undefined
      })
    }
  }
}

@sealed
class C {
  private _x = 1

  @nonenumerable()
  a = 2

  @configurable(false)
  get x() {
    return this._x
  }

  @ss(false)
  ss = 3

  @ss(false)
  ssMethod() {
  }

  // @f()
  // @g()
  @enumerable(false)
  method() {
  }
}

const c = new C()

// c.method()
console.log(JSON.stringify(c))

c.ss = 4

console.log('a', Object.getOwnPropertyDescriptor(c, 'a'))
console.log('ss', Object.getOwnPropertyDescriptor(c, 'ss'))

console.log('=================================')

const xx = {}
console.log(Object.getOwnPropertyDescriptor(xx, 'a'))
Object.defineProperty(xx, 'a', {
  value: 2,
  writable: true,
  enumerable: false,
  configurable: true,
})
console.log(Object.getOwnPropertyDescriptor(xx, 'a'))
